package prj3;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import prj3.exceptions.EmptyListException;

public class BufferedEarthGrid extends EarthGrid {

    
    protected List simulationTimeSteps;
    protected List sunAngles;
    protected List sunLatitudes;
    protected List sunOrbitAngles;
    protected List sunDistances;
    protected int maxBufferSize;
    
    {
    	simulationTimeSteps = new LinkedList();
    	sunAngles = new LinkedList();
    	sunLatitudes = new LinkedList();
    	sunOrbitAngles = new LinkedList();
    	sunDistances = new LinkedList();
    }
    /**
     * @param radius
     * @param step
     */
    public BufferedEarthGrid(float radius, float step, float excentric, float maxTilt, int maxBufferSize) {
        this(radius, step, step, excentric, maxTilt, maxBufferSize);
    }

    /**
     * @param _radius
     * @param _stepTheta
     * @param _stepPhi
     */
    public BufferedEarthGrid(float _radius, float _stepTheta, float _stepPhi, float excentric, float maxTilt, int maxBufferSize) {
    	super(_radius, _stepTheta, _stepPhi, excentric, maxTilt);
        this.maxBufferSize = maxBufferSize;
        
        /* Now we create the cells with the given spacing */
        cellMap = new HashMap();
        for(int longi=0; longi < maxLongitude; longi++) {
            for(int lati=1; lati <= maxLatitude; lati++) {
                SphericalCoordinate tC = new SphericalCoordinate(this, radius, longi, lati);
                cellMap.put(tC, new BufferedEarthCell(this, tC, maxBufferSize));
            }
        }
    }

    
    /**
     * Set the simulated timeStep.  
     * Should be called whenever the timestep is updated so it can be buffered if necessary.
     * @param timeStep
     * @return
     */
    public synchronized boolean setSimulatedTimeStep(GregorianCalendar timeStep){
        if(simulationTimeSteps.size() < maxBufferSize){
            simulationTimeSteps.add(new GregorianCalendar(timeStep.get(Calendar.YEAR), timeStep.get(Calendar.MONTH), timeStep.get(Calendar.DATE), timeStep.get(Calendar.HOUR_OF_DAY), timeStep.get(Calendar.MINUTE)));
            return true;
        }
        return false;
    }
    
    /**
     * Perform a destructive get of the Simulated Time Step (if applicable)
     * @return
     * @throws EmptyListException
     */
    public synchronized GregorianCalendar takeSimulatedTimeStep() throws EmptyListException{
		if(!isFullInit)
			fullyInit();
    	if (simulationTimeSteps.size() > 0){
            //System.out.println(simulationTimeSteps.size());
            GregorianCalendar value = (GregorianCalendar) simulationTimeSteps.remove(0);
            return value;
        }
        throw new EmptyListException();
    }
    
    public synchronized GregorianCalendar getSimulatedTimeStep(int position) throws EmptyListException {
    	
		if(!isFullInit)
			fullyInit();
		
    	java.util.Iterator it = simulationTimeSteps.iterator();
    	
    	int counter = position;
    	Object ob = null;
    	while(it.hasNext() && counter > 0) {
    		counter--;
    		ob = it.next();
    	}
    	if(counter > 0)
    		throw new EmptyListException();
    	return (GregorianCalendar)ob;
    }
	
    /**
     * Set the sun Angle.  
     * Should be called whenever the sunangle is updated so it can be buffered if necessary.
     * @param timeStep
     * @return
     */
    public synchronized boolean setSunAngle(double sunAngle){
        if(sunAngles.size() < maxBufferSize){
            this.sunAngle = sunAngle;
            sunAngles.add(new Double(sunAngle));
            return true;
        }
        return false;
    }
    
    /**
     * Perform a destructive get of the sun Angle (if applicable)
     * @return
     * @throws EmptyListException
     */
    public synchronized double takeSunAngle() throws EmptyListException{
		if(!isFullInit)
			fullyInit();
        if(sunAngles.size() > 0){
            return ((Double)sunAngles.remove(0)).doubleValue();
        }
        throw new EmptyListException();
    }

	/* (non-Javadoc)
	 * @see prj3.EarthGrid#setSunDistance(float)
	 */
	public synchronized boolean setSunDistance(double sunDistance) {
        if(sunDistances.size() < maxBufferSize){
            this.distance = sunDistance;
            sunDistances.add(new Double(sunDistance));
            return true;
        }
        return false;
	}

	/* (non-Javadoc)
	 * @see prj3.EarthGrid#setSunLatitude(float)
	 */
	public synchronized boolean setSunLatitude(double sunAngle) {
        if(sunLatitudes.size() < maxBufferSize){
            this.currentTilt = sunAngle;
            sunLatitudes.add(new Double(sunAngle));
            return true;
        }
        return false;
	}

	/* (non-Javadoc)
	 * @see prj3.EarthGrid#setSunOrbitAngle(float)
	 */
	public synchronized boolean setSunOrbitAngle(double sunAngle) {
        if(sunOrbitAngles.size() < maxBufferSize){
            sunOrbitAngles.add(new Double(sunAngle));
            return true;
        }
        return false;
	}

	/* (non-Javadoc)
	 * @see prj3.EarthGrid#takeSunDistance()
	 */
	public synchronized double takeSunDistance() throws EmptyListException {
		if(!isFullInit)
			fullyInit();
        if(sunDistances.size() > 0){
            return ((Double)sunDistances.remove(0)).doubleValue();
        }
        throw new EmptyListException();
	}

	/* (non-Javadoc)
	 * @see prj3.EarthGrid#takeSunLatitude()
	 */
	public synchronized double takeSunLatitude() throws EmptyListException {
		if(!isFullInit)
			fullyInit();
        if(sunLatitudes.size() > 0){
            return ((Double)sunLatitudes.remove(0)).doubleValue();
        }
        throw new EmptyListException();
	}

	/* (non-Javadoc)
	 * @see prj3.EarthGrid#takeSunOrbitAngle()
	 */
	public synchronized double takeSunOrbitAngle() throws EmptyListException {
		if(!isFullInit)
			fullyInit();
        if(sunOrbitAngles.size() > 0){
            return ((Double)sunOrbitAngles.remove(0)).doubleValue();
        }
        throw new EmptyListException();
	}
    
    
    
}
